package com.igeek.boot.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.igeek.boot.entity.Items;
import com.igeek.boot.mapper.ItemsMapper;
import com.igeek.boot.service.ItemsService;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.cache.annotation.*;
import org.springframework.stereotype.Service;

/**
 * @package com.igeek.boot.service.impl
 * @Description
 * @Author LittlePunk [296526219@qq.ccom]
 * @Version 2022.1
 * @Date 2023/12/20 19:52
 */
/**
 * @author chemin
 * @since 2023/12/20
 *
 * SpringBoot整合Cache缓存
 * 1.引入spring-boot-starter-cache场景启动器
 * 2.注解@EnableCaching  开启缓存
 * 3.分析自动配置原理 CacheAutoConfiguration -> CacheProperties
 *
 * SpringBoot整合Cache缓存  缓存组件替换成Redis
 * 1.引入spring-boot-starter-data-redis场景启动器
 * 2.配置application.yml文件中关于redis的连接信息
 * 3.定制CacheManager缓存规则，替换底层的组件
 *
 * CacheConfiguration缓存配置类
 *      - 默认生效SimpleCacheConfiguration
 *      - 引入Redis场景启动器后，生效RedisCacheConfiguration
 * CacheManager缓存管理器，管理各种缓存（Cache）组件
 *      - ConcurrentMapCacheManager  本地缓存管理器
 *      - RedisCacheManager          Redis缓存管理器
 * Cache缓存接口，定义缓存操作
 *      - ConcurrentMapCache         本地缓存接口
 *      - RedisCache                 Redis缓存接口
 */
//@CacheConfig  缓存统一配置，可以同一定义cacheNames指定缓存组件的名字 ， keyGenerator的key的生成器
@CacheConfig(cacheNames = "items" /*, keyGenerator = "keyGenerator"*/)
@Service
public class ItemsServiceImpl extends ServiceImpl<ItemsMapper, Items> implements ItemsService {
    /**
     * 注解 @Cacheable
     * 一、运行流程：
     * 标注的方法执行之前先来检查缓存中有没有这个数据，默认按照参数的值作为key去查询缓存，
     * 如果没有就运行方法并将结果放入缓存；以后再来调用就可以直接使用缓存中的数据。
     *
     * 二、属性：
     * 1.cacheNames/value属性：指定缓存组件的名字；将方法的返回结果放在哪个缓存中，是列表即可以指定多个缓存；
     * 2.key属性：缓存数据使用的key。默认是使用方法参数的值。
     *      2.1 默认为id传入的值，例如：1   #id
     *      2.2 使用SpEL表达式，例如：findOne[1]  即 #root.methodName+'['+#id+']'
     * 3.keyGenerator属性：key的生成器，可以指定key生成器的组件beanName。
     *      3.1 key/keyGenerator：二选一使用
     * 4.cacheManager：指定缓存管理器；或者cacheResolver指定获取解析器
     * 5.condition：指定符合条件的情况下才缓存；
     *      5.1 condition = "#id>0"：id参数的值>0的时候才进行缓存
     *      5.2 condition = "#a0>1"：第一个参数的值>1的时候才进行缓存
     * 6.unless:否定缓存；当unless指定的条件为true，方法的返回值就不会被缓存；可以获取到结果进行判断
     *      6.1 unless = "#result == null"：返回结果为null，结果不缓存；
     *      6.2 unless = "#a0==2":如果第一个参数的值是2，结果不缓存；
     * 7.sync：是否使用异步模式
     */
    @Cacheable(/*cacheNames = "items" ,*/ key = "#id")
    //@Cacheable(cacheNames = "items" , key = "#root.methodName+'['+#id+']'")
    //@Cacheable(cacheNames = "items" , keyGenerator = "keyGenerator")
    @Override
    public Items findOne(Integer id) {
        //根据id查询用户信息
        return baseMapper.selectById(id);
    }

    /**
     * 注解 @CacheEvict
     * 运行原理：清除缓存
     *
     * 属性：
     *  1.cacheNames/value属性：指定缓存组件的名字；将方法返回结果放在缓存中，是列表即可以指定多个缓存；
     *  2.key属性：指定要清除的数据
     *      2.1 默认是使用方法的参数的值，此处为id，即例如：1
     *      2.2 使用SpEL表达式，例如：#id 入参的值
     *  3.allEntries属性：true则指定清除这个缓存中所有的数据
     *  4.beforeInvocation属性：缓存的清除是否在方法之前执行
     *      4.1 false，默认代表缓存清除操作是在方法执行之后执行;如果出现异常缓存就不会清除
     *      4.2 true，代表清除缓存操作是在方法运行之前执行，无论方法是否出现异常，缓存都清除
     */
    @CacheEvict(allEntries = true , beforeInvocation = true)
    @Override
    public void delete(Integer id) {
        //模拟删除，并不是真正的删除操作
        System.out.println("执行删除delete，id = "+id);
        //baseMapper.deleteById(id);
        int i = 10/0;
    }

    //先进入方法里面，再根据返回值进行更新到缓存中
    @CachePut(/*cacheNames = "items",*/ key = "#items.id")
    @Override
    public Items update(Items items) {
        int i = baseMapper.updateById(items);
        return items;
    }


    //复杂的缓存规则，相当于先去以name作为key查找缓存数据，若没有找到则直接查询数据库，将数据库中返回的数据以id、price作为key存储至Cache缓存中
    @Caching(
            cacheable = @Cacheable(key = "#name"),
            put = {
                    @CachePut(key = "#result.id") ,
                    @CachePut(key = "#result.price")
            }
    )
    @Override
    public Items findByName(String name) {
        LambdaQueryWrapper<Items> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Items::getName , name);
        Items items = baseMapper.selectOne(lambdaQueryWrapper);
        return items;
    }

}
